#lang racket

(require "box.scm")

(provide env env-bindings env-specials set-env-bindings! set-env-specials!
	 make-binding make-dummy-binding
	 extend-env extend-env* update-env! update-env!*
	 special? bound? env-ref)

;;; environments
;;

;; an environment is the mapping of variable names -> values
;; during resolve we just make dummy bindings, everything is mapped to #f
;; envs also hold the list of unshadowed special forms

(struct env ((bindings #:mutable)
	     (specials #:mutable))
	#:transparent)

(define (make-binding key value)
  (cons key (box value)))

(define (make-dummy-binding key)
  (make-binding key #f))

(define (extend-env env^ binding)
  (env (cons binding (env-bindings env^))
       (remove (car binding) (env-specials env^))))

(define (extend-env* env^ bindings)
  (env (append bindings (env-bindings env^))
       (remove* (map car bindings) (env-specials env^))))

(define (update-env! env binding)
  (set-env-bindings! env (cons binding (env-bindings env)))
  (set-env-specials! env (remove (car binding) (env-specials env)))
  env)

(define (update-env!* env bindings)
  (set-env-bindings! env (append bindings (env-bindings env)))
  (set-env-specials! env (remove* (map car bindings) (env-specials env)))
  env)

(define (special? s env)
  (member s (env-specials env)))

(define (bound? var env)
  (assoc var (env-bindings env)))

(define (env-ref key env)
  (let ((binding (assoc key (env-bindings env))))
    (unless binding
	    (error "env-ref: no such key" key))
    (box-value (cdr binding))))

